using System;
using System.Collections.Generic;
using NUnit.Framework;
using TypeMock;

namespace ValidationFramework.Tests
{
    [TestFixture]
	public class MemberValidationManagerTest
	{

		public class MockMemberValidationManager : MemberValidationManager<MockInfoDescriptor>
		{
			

			public MockMemberValidationManager()
				: base("foo")
			{
			}
			public MockMemberValidationManager(object target)
				: base(target)
			{
			}

		
			public MockMemberValidationManager(RuntimeTypeHandle targetHandle)
				: base(targetHandle)
			{
			}

			public Dictionary<Rule, ValidationError> ErrorDictionary
			{
				get
				{
					return errorDictionary;
				}
			}
			public AutoKeyDictionary<string, MockInfoDescriptor> members;
			public override AutoKeyDictionary<string, MockInfoDescriptor> Members
			{
				get
				{
					return members;
				}
			}

            public string Property { get; set; }
		}

		[TestFixture]
		public class InternalThrowException : BaseTypeMock
		{
			[Test]
			public void Default()
			{
				var memberValidationManager = new MockMemberValidationManager();

				//Mock mock = MockManager.MockAll(typeof(RuleCollection));
				//mock.ExpectAndReturn("TryGetRulesForRuleSet", true, new[] {typeof (string), typeof (IList<Rule>)});
				var infoDescriptor = new MockInfoDescriptor {key = "key"};
			    infoDescriptor.Rules.Add(new MockRule(), null);
				using (RecorderManager.StartRecording())
				{
					MemberValidationManager<MockInfoDescriptor>.ThrowNoRules(null, false,false);
				}
				memberValidationManager.InternalThrowException(infoDescriptor, null,null, false);
			}


			[Test]
			[ExpectedException(typeof(ArgumentException))]
			public void ExceptionRuleInvalid()
			{
				var memberValidationManager = new MockMemberValidationManager();

				//Mock mock = MockManager.MockAll(typeof(RuleCollection));
				//mock.ExpectAndReturn("TryGetRulesForRuleSet", true, new[] {typeof (string), typeof (IList<Rule>)});
				var infoDescriptor = new MockInfoDescriptor {key = "key"};
			    var mockRule = new MockRule();
				mockRule.validationResult = new ValidationError(mockRule, "error", null, infoDescriptor);
                infoDescriptor.Rules.Add(mockRule, null);
				using (RecorderManager.StartRecording())
				{
					MemberValidationManager<MockInfoDescriptor>.ThrowNoRules(null, false, false);
				}
				memberValidationManager.InternalThrowException(infoDescriptor, null, null, false);
			}
		}


		[TestFixture]
		public class GetResultsInErrorForList : BaseTypeMock
		{

			[Test]
			[ExpectedException(typeof(ArgumentNullException))]
			public void ExceptionNullMemberNames()
			{
				var memberValidationManager = new MockMemberValidationManager();
				memberValidationManager.GetResultsInErrorForList(null);
			}

			[Test]
			[ExpectedException(typeof(ArgumentOutOfRangeException))]
			public void ExceptionZeroLengthMemberNames()
			{
				var memberValidationManager = new MockMemberValidationManager();
				memberValidationManager.GetResultsInErrorForList(new string[] { });
			}
			[Test]
			public void Default()
			{
				var list = new List<ValidationError>();
				var memberValidationManager = new MockMemberValidationManager();

				using (var recorder = RecorderManager.StartRecording())
				{
					recorder.ExpectAndReturn(memberValidationManager.GetResultsInError("foo"), list);
				}
				memberValidationManager.GetResultsInErrorForList("foo");
			}
		}

		[TestFixture]
		public class GetResultsInError : BaseTypeMock
		{
			[Test]
			[ExpectedException(typeof(ArgumentNullException))]
			public void ExceptionNullMemberName()
			{
				var memberValidationManager = new MockMemberValidationManager();
				memberValidationManager.GetResultsInError(null);
			}
			[Test]
			[ExpectedException(typeof(ArgumentException))]
			public void ExceptionEmptyMemberName()
			{
				var memberValidationManager = new MockMemberValidationManager();
				memberValidationManager.GetResultsInError(string.Empty);
			}

			[Test]
			public void Default()
			{
				var memberValidationManager = new MockMemberValidationManager();
				IList<ValidationError> list;
				using (var recorder = RecorderManager.StartRecording())
				{
					recorder.ExpectAndReturn(memberValidationManager.TryGetResultsInError("foo", out list), true);
				}
				memberValidationManager.GetResultsInError("foo");
			}

			[Test]
			public void Expression()
			{
				var memberValidationManager = new MockMemberValidationManager();
				const IList<ValidationError> list = null;
				using (var recorder = RecorderManager.StartRecording())
				{
					recorder.ExpectAndReturn(memberValidationManager.GetResultsInError("Property"), list);
				}
				memberValidationManager.GetResultsInError(() => memberValidationManager.Property);
			}
			[Test]
			[ExpectedException(typeof(ArgumentException))]
			public void ExceptionMemberNotExist()
			{
				var memberValidationManager = new MockMemberValidationManager();
			    using (var recorder = RecorderManager.StartRecording())
				{
				    IList<ValidationError> list;
				    recorder.ExpectAndReturn(memberValidationManager.TryGetResultsInError("foo", out list), false);
				}
			    memberValidationManager.GetResultsInError("foo");
			}
		}


        [TestFixture]
        public class CheckValidMemberForRuleSet
        {
            public class TempClass
            {

                [RangeIntRule(3, 4, RuleSet = "Car", ErrorMessage = "A car should have between 3 and 4 wheels.")]
                [RangeIntRule(2, 4, RuleSet = "Bike", ErrorMessage = "A bike should have between 2 and 4 wheels.")]
                [RangeIntRule(4, 12, RuleSet = "Truck", ErrorMessage = "A truck should have between 4 and 12 wheels.")]
                public int Property
                {
                    get;
                    set;
                }
            }

            [Test]
            public void Test()
            {
                var tempClass = new TempClass();

				var carPropertyValidationManager = new PropertyValidationManager(tempClass)
				{
					RuleSet = "car"
				};
                carPropertyValidationManager.ValidateAll();
                Assert.AreEqual(1, carPropertyValidationManager.ValidatorResultsInError.Count);
                Assert.AreEqual("A car should have between 3 and 4 wheels.", carPropertyValidationManager.ValidatorResultsInError[0].ErrorMessage);

                var truckPropertyValidationManager = new PropertyValidationManager(tempClass)
                                                     	{
                                                     		RuleSet = "truck"
                                                     	};
                truckPropertyValidationManager.ValidateAll();
                Assert.AreEqual(1, truckPropertyValidationManager.ValidatorResultsInError.Count);
                Assert.AreEqual("A truck should have between 4 and 12 wheels.", truckPropertyValidationManager.ValidatorResultsInError[0].ErrorMessage);

				var bikePropertyValidationManager = new PropertyValidationManager(tempClass)
				{
					RuleSet = "bike"
				};
                bikePropertyValidationManager.ValidateAll();
                Assert.AreEqual(1, bikePropertyValidationManager.ValidatorResultsInError.Count);
                Assert.AreEqual("A bike should have between 2 and 4 wheels.", bikePropertyValidationManager.ValidatorResultsInError[0].ErrorMessage);

            }
        }
  [TestFixture]
		public class TryGetResultsInError : BaseTypeMock
		{
			[Test]
			[ExpectedException(typeof(ArgumentNullException))]
			public void ExceptionNullMemberName()
			{
				var memberValidationManager = new MockMemberValidationManager();
				IList<ValidationError> list;
				memberValidationManager.TryGetResultsInError(null, out list);
			}
			[Test]
			[ExpectedException(typeof(ArgumentException))]
			public void ExceptionEmptyMemberName()
			{
				var memberValidationManager = new MockMemberValidationManager();
				IList<ValidationError> list;
				memberValidationManager.TryGetResultsInError(string.Empty, out list);
			}

		

			[Test]
			public void Expression()
			{
				var memberValidationManager = new MockMemberValidationManager();
				IList<ValidationError> list;
				using (var recorder = RecorderManager.StartRecording())
				{
					recorder.ExpectAndReturn(memberValidationManager.TryGetResultsInError("Property", out list), true);
				}
                memberValidationManager.TryGetResultsInError(() => memberValidationManager.Property, out list);
			}


		}


		[TestFixture]
		public class ThrowException : BaseTypeMock
		{
			[Test]
			[ExpectedException(typeof(ArgumentNullException))]
			public void ExceptionNullMemberName()
			{
				var memberValidationManager = new MockMemberValidationManager();
				memberValidationManager.ThrowException(null, null, null);
			}

			[Test]
			[ExpectedException(typeof(ArgumentException))]
			public void ExceptionEmptyMemberName()
			{
				var memberValidationManager = new MockMemberValidationManager();
				memberValidationManager.ThrowException(null, string.Empty, null);
			}

			[Test]
			[ExpectedException(typeof(ArgumentException))]
			public void ExceptionMemberNotFound()
			{
				var memberValidationManager = new MockMemberValidationManager();
			    using (var recorder = RecorderManager.StartRecording())
				{
				    MockInfoDescriptor infoDescriptor;
				    recorder.ExpectAndReturn(memberValidationManager.Members.TryGetValue("a", out infoDescriptor), false);
				}
			    memberValidationManager.ThrowException(null, "a", null);
			}

			[Test]
			public void Default()
			{
				var mock = MockManager.Mock(typeof(MockMemberValidationManager));
				var memberValidationManager = new MockMemberValidationManager();

				MockInfoDescriptor infoDescriptor;

				const string expectedMemberNalue = "expectedMemberName";
				using (var recorder = RecorderManager.StartRecording())
				{
					recorder.ExpectAndReturn(memberValidationManager.Members.TryGetValue(expectedMemberNalue, out infoDescriptor), true);
				}
				var parameters = mock.ExpectCall("InternalThrowException");
				const string expectedMemberValue = "expectedMemberValue";
				var expectedContext = new object();
				parameters.Args(infoDescriptor, expectedMemberValue, expectedContext, true);
				memberValidationManager.ThrowException(expectedMemberValue, expectedMemberNalue, expectedContext);
				mock.Verify();
			}

			[Test]
			public void Expression()
			{
				var memberValidationManager = new MockMemberValidationManager();
				const string expectedMemberNalue = "Property";
				const string expectedMemberValue = "expectedMemberValue";
				var expectedContext = new object();
			    using (RecorderManager.StartRecording())
				{
					memberValidationManager.ThrowException(expectedMemberNalue, expectedMemberNalue, expectedContext);
				}
				memberValidationManager.ThrowException(expectedMemberValue, ()=> memberValidationManager.Property, expectedContext);
			}
		}
	
		[TestFixture]
		public class ThrowNoRules : BaseTypeMock
		{
			[Test]
			public void Default1()
			{
				MemberValidationManager<MockInfoDescriptor>.ThrowNoRules(null, false, false);
			}
			[Test]
			public void Default2()
			{
				MemberValidationManager<MockInfoDescriptor>.ThrowNoRules(null, true, false);
			}
			[Test]
			[ExpectedException(typeof(InvalidOperationException))]
			public void Exception1()
			{
				MemberValidationManager<MockInfoDescriptor>.ThrowNoRules(null, false, true);
			}
			[Test]
			[ExpectedException(typeof(InvalidOperationException))]
			public void Exception2()
			{
				MemberValidationManager<MockInfoDescriptor>.ThrowNoRules("Not Null", false, true);
			}
			[Test]
			public void Default3()
			{
				MemberValidationManager<MockInfoDescriptor>.ThrowNoRules(null, true, true);
			}
		}

		[TestFixture]
		public class TryThrowException : BaseTypeMock
		{
			[Test]
			[ExpectedException(typeof(ArgumentNullException))]
			public void ExceptionNullMemberName()
			{
				var memberValidationManager = new MockMemberValidationManager();
				var memberName = (string)null;
				memberValidationManager.TryThrowException(null, memberName, null);
			}

			[Test]
			[ExpectedException(typeof(ArgumentException))]
			public void ExceptionEmptyMemberName()
			{
				var memberValidationManager = new MockMemberValidationManager();
				memberValidationManager.TryThrowException(null, string.Empty, null);
			}

			[Test]
			public void MemberNotFound()
			{
				var memberValidationManager = new MockMemberValidationManager();
				MockInfoDescriptor infoDescriptor;
				using (var recorder = RecorderManager.StartRecording())
				{
					recorder.ExpectAndReturn(memberValidationManager.Members.TryGetValue("a", out infoDescriptor), false);
				}
				memberValidationManager.TryThrowException(null, "a", null);
			}

			[Test]
			public void Default()
			{
				const string expectedMemberNalue = "expectedMemberName";
				const string expectedMemberValue = "expectedMemberValue";
				var expectedContext = new object();

				var mock = MockManager.Mock(typeof(MockMemberValidationManager));
				var memberValidationManager = new MockMemberValidationManager();

				MockInfoDescriptor infoDescriptor;

				using (var recorder = RecorderManager.StartRecording())
				{
					recorder.ExpectAndReturn(memberValidationManager.Members.TryGetValue(expectedMemberNalue, out infoDescriptor), true);
				}
				var parameters = mock.ExpectCall("InternalThrowException");
				parameters.Args(infoDescriptor, expectedMemberValue, expectedContext, false);
				memberValidationManager.TryThrowException(expectedMemberValue, expectedMemberNalue, expectedContext);
				mock.Verify();
			}

			[Test]
			public void Expression()
			{
				const string expectedMemberNalue = "expectedMemberName";
				const string expectedMemberValue = "expectedMemberValue";
				var expectedContext = new object();
				var memberValidationManager = new MockMemberValidationManager();

				using (RecorderManager.StartRecording())
				{
					memberValidationManager.TryThrowException(expectedMemberNalue,"Property", expectedContext);
				}
				memberValidationManager.TryThrowException(expectedMemberValue, () => memberValidationManager.Property, expectedContext);
			}
		}
		[TestFixture]
		public class TryValidate : BaseTypeMock
		{
			[Test]
			[ExpectedException(typeof(ArgumentNullException))]
			public void ExceptionNullMemberName()
			{
				var memberValidationManager = new MockMemberValidationManager();
				memberValidationManager.TryValidate(null);
			}

			[Test]
			[ExpectedException(typeof(ArgumentException))]
			public void ExceptionEmptyMemberName()
			{
				var memberValidationManager = new MockMemberValidationManager();
				memberValidationManager.TryValidate(string.Empty);
			}

			[Test]
			public void MemberNotFound()
			{
				const string expectedMemberNalue = "expectedMemberName";
				var memberValidationManager = new MockMemberValidationManager();
				MockInfoDescriptor infoDescriptor;
				using (var recorder = RecorderManager.StartRecording())
				{
					recorder.ExpectAndReturn(memberValidationManager.Members.TryGetValue("a", out infoDescriptor), false);
				}
				Assert.IsFalse(memberValidationManager.TryValidate(expectedMemberNalue));
			}
			[Test]
			public void Expression()
			{
				var memberValidationManager = new MockMemberValidationManager();
				using (var recorder = RecorderManager.StartRecording())
				{
					recorder.ExpectAndReturn(memberValidationManager.TryValidate("Property"), false);
				}
				Assert.IsFalse(memberValidationManager.TryValidate(() => memberValidationManager.Property));
			}

			[Test]
			public void MemberExists()
			{
				const string expectedMemberValue = "expectedMemberValue";
				const string expectedMemberNalue = "expectedMemberName";

				var mock = MockManager.Mock(typeof(MockMemberValidationManager));
				var memberValidationManager = new MockMemberValidationManager();

				var infoDescriptor = new MockInfoDescriptor {value = expectedMemberValue};
			    using (var recorder = RecorderManager.StartRecording())
				{
					recorder.ExpectAndReturn(memberValidationManager.Members.TryGetValue(expectedMemberNalue, out infoDescriptor), true);
				}
				var parameters = mock.ExpectAndReturn("CheckValidMember", true);
				parameters.Args(infoDescriptor, expectedMemberValue);
				Assert.IsTrue(memberValidationManager.TryValidate(expectedMemberNalue));
				mock.Verify();
			}

		}
		[TestFixture]
		public class Validate : BaseTypeMock
		{
			[Test]
			[ExpectedException(typeof(ArgumentNullException))]
			public void ExceptionNullMemberName()
			{
				var memberValidationManager = new MockMemberValidationManager();
				memberValidationManager.Validate(null);
			}

			[Test]
			[ExpectedException(typeof(ArgumentException))]
			public void ExceptionEmptyMemberName()
			{
				var memberValidationManager = new MockMemberValidationManager();
				memberValidationManager.Validate(string.Empty);
			}

			[Test]
			[ExpectedException(typeof(ArgumentException))]
			public void ExceptionMemberNotFound()
			{
				var memberValidationManager = new MockMemberValidationManager();
				MockInfoDescriptor infoDescriptor;
				using (var recorder = RecorderManager.StartRecording())
				{
					recorder.ExpectAndReturn(memberValidationManager.Members.TryGetValue("a", out infoDescriptor), false);
				}
				memberValidationManager.Validate("a");
			}

			[Test]
			[ExpectedException(typeof(ArgumentException))]
			public void ExceptionMemberNotFoundWithRuleSet()
			{
				const string expectedMemberValue = "expectedMemberValue";
				const string expectedMemberNalue = "expectedMemberName";

				var mock = MockManager.Mock(typeof(MockMemberValidationManager));
				var memberValidationManager = new MockMemberValidationManager();

				var infoDescriptor = new MockInfoDescriptor {value = expectedMemberValue};
			    using (var recorder = RecorderManager.StartRecording())
				{
					recorder.ExpectAndReturn(memberValidationManager.Members.TryGetValue(expectedMemberNalue, out infoDescriptor), true);
				}
				var parameters = mock.ExpectAndReturn("CheckValidMember", false);
				parameters.Args(infoDescriptor, expectedMemberValue);
				memberValidationManager.Validate(expectedMemberNalue);
			}

			[Test]
			public void MemberExists()
			{
				const string expectedMemberValue = "expectedMemberValue";
				const string expectedMemberNalue = "expectedMemberName";

				var mock = MockManager.Mock(typeof(MockMemberValidationManager));
				var memberValidationManager = new MockMemberValidationManager();

				var infoDescriptor = new MockInfoDescriptor {value = expectedMemberValue};
			    using (var recorder = RecorderManager.StartRecording())
				{
					recorder.ExpectAndReturn(memberValidationManager.Members.TryGetValue(expectedMemberNalue, out infoDescriptor), true);
				}
				var parameters = mock.ExpectAndReturn("CheckValidMember", true);
				parameters.Args(infoDescriptor, expectedMemberValue);
				memberValidationManager.Validate(expectedMemberNalue);
			}

		}

		[TestFixture]
		public class ValidateAll : BaseTypeMock
		{
			[Test]
			public void RuleFound()
			{
				const string expectedMemberValue = "expectedMemberValue";

				var memberValidationManager = new MockMemberValidationManager();
				var mock = MockManager.MockAll(typeof(MockMemberValidationManager));

				var infoDescriptor = new MockInfoDescriptor {value = expectedMemberValue, key = "key"};
			    memberValidationManager.members = new MockInfoDescriptorCollection {infoDescriptor};
			    var parameters = mock.ExpectAndReturn("CheckValidMember", true);
				parameters.Args(infoDescriptor, expectedMemberValue);
				using (RecorderManager.StartRecording())
				{
					MemberValidationManager<MockInfoDescriptor>.ThrowNoRules(null, true, true);
				}
				memberValidationManager.ValidateAll();
				mock.Verify();
			}
			[Test]
			public void RuleNotFound()
			{
				const string expectedMemberValue = "expectedMemberValue";

				var memberValidationManager = new MockMemberValidationManager();
				var mock = MockManager.MockAll(typeof(MockMemberValidationManager));

				var infoDescriptor = new MockInfoDescriptor {value = expectedMemberValue, key = "key"};
			    memberValidationManager.members = new MockInfoDescriptorCollection {infoDescriptor};
			    var parameters = mock.ExpectAndReturn("CheckValidMember", false);
				parameters.Args(infoDescriptor, expectedMemberValue);
				using (RecorderManager.StartRecording())
				{
					MemberValidationManager<MockInfoDescriptor>.ThrowNoRules(null, false, true);
				}
				memberValidationManager.ValidateAll();
				mock.Verify();
			}

		}
		[TestFixture]
		public class TryValidateAll : BaseTypeMock
		{
			[Test]
			public void RuleFound()
			{
				const string expectedMemberValue = "expectedMemberValue";

				var memberValidationManager = new MockMemberValidationManager();
				var mock = MockManager.MockAll(typeof(MockMemberValidationManager));

				var infoDescriptor = new MockInfoDescriptor {value = expectedMemberValue, key = "key"};
			    memberValidationManager.members = new MockInfoDescriptorCollection {infoDescriptor};
			    var parameters = mock.ExpectAndReturn("CheckValidMember", true);
				parameters.Args(infoDescriptor, expectedMemberValue);
				memberValidationManager.TryValidateAll();
				mock.Verify();
			}
			[Test]
			public void RuleNotFound()
			{
				const string expectedMemberValue = "expectedMemberValue";

				var memberValidationManager = new MockMemberValidationManager();
				var mock = MockManager.MockAll(typeof(MockMemberValidationManager));

				var infoDescriptor = new MockInfoDescriptor {value = expectedMemberValue, key = "key"};
			    memberValidationManager.members = new MockInfoDescriptorCollection {infoDescriptor};
			    var parameters = mock.ExpectAndReturn("CheckValidMember", false);
				parameters.Args(infoDescriptor, expectedMemberValue);
				memberValidationManager.TryValidateAll();
				mock.Verify();
			}

		}
	}
}